/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package compilador_full;

import SemanticoPacote.Token;
import java.util.ArrayList;
import java.util.Hashtable;

/**
 *
 * @author Jomas
 */
public class TabelaSimbolos {

    // hash para as tokens
    private Hashtable<Integer, String> Tokens = new Hashtable<Integer, String>();
    private Hashtable<Integer, Integer> Linhas = new Hashtable<Integer, Integer>();
    private Hashtable<Integer, String> Tipo = new Hashtable<Integer, String>();
    private Hashtable<Integer, String> Escopo = new Hashtable<Integer, String>();
    // private Hashtable<Integer, String> Struct = new Hashtable<Integer, String>();
    private Hashtable<Integer, String> Categoria = new Hashtable<Integer, String>();
    private Hashtable<Integer, String> TipoVariavel = new Hashtable<Integer, String>();
    private Hashtable<Integer, String> TamanhoVetor = new Hashtable<Integer, String>();
    private Hashtable<Integer, String> unic = new Hashtable<Integer, String>();
    private Hashtable<Integer, String> Escopo2 = new Hashtable<Integer, String>();
    private Hashtable<Integer, ArrayList<String>> ParametrosMetodo = new Hashtable<Integer, ArrayList<String>>();
    //variaveis para sintatico
    private int sav;
    private String setEscopo;
    private String setEscopo2;
    private int ponteiro;
    private int ponteiroParam;
    private String Lookahead;
    private String tipoTipo;
    //variaveis semantico
    private int indiceUnicidade;
    private int posicaoInicial;
    private ArrayList<String> listaParametros = new ArrayList<String>();
    //estrutura para analise semantica
    private ArrayList<Token> Unicidade = new ArrayList<Token>();
    private ArrayList<String> variaveis = new ArrayList<String>();//Auxiliar para verificar unicidade
    private ArrayList<String> globaisVariaveis = new ArrayList<String>();//Auxiliar para verificar unicidade variaveis globais

    public TabelaSimbolos() {
        sav = 0;
        ponteiro = 0;
        ponteiroParam = 0;
        Lookahead = "";
        setEscopo = "";
        tipoTipo = "";
        setEscopo2 = "";
    }

    public Hashtable<Integer, ArrayList<String>> getParametrosMetodo() {
        return ParametrosMetodo;
    }

    public void setParametrosMetodo(Hashtable<Integer, ArrayList<String>> ParametrosMetodo) {
        this.ParametrosMetodo = ParametrosMetodo;
    }

    public Hashtable<Integer, String> get2Escopo2() {
        return Escopo2;
    }

    //
    public void put2Escopo2(String escopoooo) {
        this.Escopo2.put(ponteiro, escopoooo);
    }
    //

    public String superBuscaEscopo() {
        int retorna = 0;
        while (!Tokens.get(ponteiro + retorna).equals("}")) {
            retorna++;
        }
        // retorna o dado
        return Tokens.get(ponteiro + retorna + 1);
    }
    //

    //
    public Hashtable<Integer, String> getTamanhoVetor() {
        return TamanhoVetor;
    }

    public void setTamanhoVetor(Hashtable<Integer, String> TamanhoVetor) {
        this.TamanhoVetor = TamanhoVetor;
    }

    // insere algo na tabela
    public void insert(String token, String tipo, int linha) {
        Linhas.put(sav, linha);
        Tipo.put(sav, tipo);
        Tokens.put(sav++, token);
    }

    // mostra o tamanho da tabela
    public int sizeTabela() {
        return Tokens.size();
    }
    // imprime da tabela

    public String printTabela(int i) {
        String retorno = Tokens.get(i) + "         : " + Tipo.get(i) + "                       -   <Linha " + Linhas.get(i) + ">";
        return retorno;
    }
    //
    // ------------------------------------
    // parte sintática

    // seta ponteiro
    public void ZeraLookahead() {
        ponteiro = 0;
        Lookahead = Tokens.get(0);
    }
    // retorna ponteiro

    public int getponteiroSize() {
        return ponteiro;
    }
    // reconhecer

    public boolean reconhecer(String var) {
        if (Lookahead.equals(var)) {
            ponteiro++;
            if (Tokens.containsKey(ponteiro)) {
                Lookahead = Tokens.get(ponteiro);
            } else {
                Lookahead = "";
            }
            return true;
        } else {
            return false;
        }
    }
    // ver lookahead

    public String getLookahead() {
        if (Tokens.containsKey(ponteiro)) {
            Lookahead = Tokens.get(ponteiro);
        } else {
            Lookahead = "";
        }
        return Lookahead;
    }
    //

    // retorna tipo da vez
    public String getTipo() {
        if (Tipo.containsKey(ponteiro)) {
            return Tipo.get(ponteiro);
        } else {
            return "";
        }
    }
    // retorna ponteiro

    public int getPonteiro() {
        return ponteiro;
    }
    // retorna linha

    public int getLinha() {
        if (Linhas.containsKey(ponteiro - 1)) {
            return Linhas.get(ponteiro - 1);
        } else {
            return -1;
        }
    }
    // apenas consome sem verificar

    public void soConsome() {
        ponteiro++;
        if (Tokens.containsKey(ponteiro)) {
            Lookahead = Tokens.get(ponteiro);
        } else {
            Lookahead = "";
        }
    }
    //
    // ver lookahead adiante

    public String superLookahead(int look) {
        if (Tokens.containsKey(ponteiro + look)) {
            Lookahead = Tokens.get(ponteiro + look);
        } else {
            Lookahead = "";
        }
        return Lookahead;
    }
    //
    // retorna tipo adiante

    public String superTipo(int look) {
        if (Tipo.containsKey(ponteiro + look)) {
            return Tipo.get(ponteiro + look);
        } else {
            return "";
        }
    }
    // -------------------------------------------------------------------------------------------------------------------

    // Parte semantica 
    // ***
    // Parte referente ao escopo
    // escopo pode ser global ou o nome do metodo
    public void salvaEscopo(String escopo) {
        setEscopo = escopo;
    }
    // escopo pode ser global, struct

    public void insertEscopo() {
        Escopo.put(ponteiro, setEscopo);
    }
    //
    // retorna escopo

    public String getEscopo() {
        if (Escopo.containsKey(ponteiro)) {
            return Escopo.get(ponteiro);
        } else {
            return "";
        }
    }
    //

    // escopo pode ser global ou o nome do metodo
    public void salvaEscopo2(String escopo) {
        setEscopo2 = escopo;
    }
    // escopo pode ser global, struct

    public void insertEscopo2() {
        Escopo.put(ponteiro, setEscopo2);
    }

    // ***
    // Parte referente a categoria
    // a categoria pode ser metodo, variavel, struct, constante,
    public void insertCategoria(String categoria) {
        Categoria.put(ponteiro, categoria);
    }
    // retorna categoria

    public String getCategoria() {
        if (Categoria.containsKey(ponteiro)) {
            return Categoria.get(ponteiro);
        } else {
            return "";
        }
    }
    //

    // ***
    // Metodos referentes a Tipo Variável
    // salva o tipo anterior
    public void salvaTipo(String tipoTipo) {
        this.tipoTipo = tipoTipo;
    }
    //tabela que insere o tipo da variavel

    public void insertTipoVariavel() {
        TipoVariavel.put(ponteiro, tipoTipo);
    }
    // retorna o tipo variável

    public String getTipoVariavel() {
        if (TipoVariavel.containsKey(ponteiro)) {
            return TipoVariavel.get(ponteiro);
        } else {
            return "";
        }
    }
    //

// ----------------PARTE SEMANTICA-------------------------------------------------------------------------
    // insere token na tabela para unicidade
    public boolean insertUnicidade(String token) {
        if (!unic.containsValue(token)) {
            unic.put(indiceUnicidade++, token);
            return true;
        } else {
            return false;
        }
    }

    //CHAMAR ESSE METODO antes da primeira declaracao de metodod
    public void setPosicaoInicial() {
        this.posicaoInicial = this.indiceUnicidade;
    }

    //REMOVE AS VARIAVEIS INTERNAS A METODOS
    public void removeVariaveis() {

        for (int i = this.posicaoInicial; i < unic.size(); i++) {
            unic.remove(i);
        }

        indiceUnicidade = unic.size();
    }

    public boolean jaContemAquelaVariavel(String variavel) {


        if (!variaveis.contains(variavel)) {
            variaveis.add(variavel);
            return false;// retorna false pq nao tinha
        } else {
            return true;//ja contém aquela variável
        }
    }

    public boolean jaContemAquelaVariavelGlobal(String variavel) {
        if (!globaisVariaveis.contains(variavel)) {
            globaisVariaveis.add(variavel);
            return false;// retorna false pq nao tinha
        } else {
            return true;//ja contém aquela variável global
        }
    }

    public void preencheArray() {

        for (int i = 0; i < this.getponteiroSize(); i++) {
            String categoria = "";
            String escopo = "";
            String escopo2 = "";
            String tipovariavel = "";
            String tamanhoVetor = "";
            Token token = new Token();

            if (this.getTabelaEscopo().containsKey(i) || this.getTabelaCategoria().containsKey(i) || this.getTabelaTipoVariavel().containsKey(i) || this.getTamanhoVetor().containsKey(i) || this.get2Escopo2().containsKey(i) || this.getTabelaTipoVariavel().containsKey(i) || this.getParametrosMetodo().containsKey(i)) {

                if (this.getTabelaEscopo().containsKey(i)) {
                    escopo = this.getTabelaEscopo().get(i);
                }

                if (this.getTabelaCategoria().containsKey(i)) {
                    categoria = this.getTabelaCategoria().get(i);
                }

                if (this.getTabelaCategoria().containsKey(i)) {
                    tamanhoVetor = this.getTamanhoVetor().get(i);
                }

                if (this.getTabelaTipoVariavel().containsKey(i)) {
                    tipovariavel = this.getTabelaTipoVariavel().get(i);
                }

                if (this.getTabelaTipoVariavel().containsKey(i)) {
                    escopo2 = this.get2Escopo2().get(i);
                }

                if (this.getParametrosMetodo().containsKey(i)) {
                    for (int j = 0; j < this.getParametrosMetodo().get(i).size(); j++) {
                        token.getParametros().add(this.getParametrosMetodo().get(i).get(j));
                    }

                }




                token.setLexema(this.getLexema(i));
                token.setCategoria(categoria);
                token.setEscopo1(escopo);
                token.setEscopo2(escopo2);
                token.setTamanhoVetor(tamanhoVetor);
                token.setTipoVariavel(tipovariavel);


                Unicidade.add(token); //adicionando token no array

            }

        }

    }

    public void imprimeTodosTokens() {
        for (int i = 0; i < Unicidade.size(); i++) {
            System.out.print("Lexema " + Unicidade.get(i).getLexema() + " \n" +
                    " Categoria " + Unicidade.get(i).getCategoria() + " \n" +
                    " Escopo1 " + Unicidade.get(i).getEscopo1() + " \n" +
                    " Escopo2 " + Unicidade.get(i).getEscopo2() + " \n");
            if (Unicidade.get(i).getParametros().size() > 0) {
                for (int j = 0; j < Unicidade.get(i).getParametros().size(); j++) {
                    System.out.print(" Parametro " + j + " " + Unicidade.get(i).getParametros().get(j) + " ");
                }
            }

            System.out.println("\n");


        }
    }

    /**
     *
     * @param escopo
     * @param lexema
     * @return false, se já ha variavel global declarada com o mesmo nome
     */
    public boolean verificaGlobais(String lexema) {

        boolean achouGlobal = false;


        for (int i = 0; i < this.Unicidade.size(); i++) {


            //já tem uma global com
            if (Unicidade.get(i).getLexema().equals(lexema)) {

                if (Unicidade.get(i).getEscopo1().equals("global") && achouGlobal == false) {
                    achouGlobal = true;
                } else {
                    return false;
                }
            }

        }

        return true;
    }

    /**
     *
     * @param lexema
     * @param escopo1
     * @return false, se já ha mesma variavel declarada dentro de um mesmo método
     */
    public boolean verificaMetodoEMetodo(String lexema, String escopo1) {
        boolean achouLocal = false;

        for (int i = 0; i < this.Unicidade.size(); i++) {
            if (Unicidade.get(i).getEscopo1().equals(escopo1) && Unicidade.get(i).getLexema().equals(lexema)) {
                for (int j = i + 1; j < this.Unicidade.size(); j++) {
                    if (Unicidade.get(j).getEscopo1().equals(escopo1) && Unicidade.get(j).getLexema().equals(lexema)) {
                        return false;
                    }
                }

            }

        }

        return true;
    }

    public boolean verificaSeFoiDeclaradoComoGlobal(String lexema) {

        for (int i = 0; i < this.Unicidade.size(); i++) {
            //já tem uma global com
            if (Unicidade.get(i).getLexema().equals(lexema)) {
                if (Unicidade.get(i).getEscopo1().equals("global")) {
                    return true; //se axou a declaração
                }
            }
        }
        return false;//se não achou a declaração
    }

    /**
     *
     * @param lexema
     * @param metodo
     * @return Verifica se variavel foi declarada dentro de um metodo
     */
    public boolean verificaSeJaFoiDeclarado(String lexema, String metodo) {

        for (int i = 0; i < this.Unicidade.size(); i++) {
            if (Unicidade.get(i).getLexema().equals(lexema)) {
                if (Unicidade.get(i).getEscopo1().equals(metodo)) {
                    return true;
                }
            }
        }
        return false;
    }

    public boolean verificaSeJaFoiDeclaradoEscopo2(String lexema, String escopo22) {
        System.out.println("vindo: " + lexema + " " + escopo22);
        boolean achouGlobal = false;
        String escopo2 = null;

        for (int i = 0; i < this.Unicidade.size(); i++) {


            //já tem uma global com
            if (Unicidade.get(i).getLexema().equals(lexema)) {
                System.out.println("escopo2: " + Unicidade.get(i).getEscopo2());
                escopo2 = Unicidade.get(i).getEscopo2();
                if (escopo2 != null){
                    if (Unicidade.get(i).getEscopo2().equals(escopo22) && achouGlobal == false) {
                        achouGlobal = true;
                    } else {
                        return false;
                    }
                }

            }

        }

        return true;
    }

    public boolean verificaDuplicidadeDeMetodo(String lexema) {

        for (int i = 0; i < this.Unicidade.size(); i++) {
            if (Unicidade.get(i).getLexema().equals(lexema)) {
                return false;
            }
        }
        return true;
    }

    /**Esse método só pode ser chamado quando se verifica a semântica dentro de um método.
     *
     * @param lexema
     * @return Retorna false se dentro de um metodo existe uma variavel já declarada como global
     */
    public boolean verificaMetodoEGlobal(String lexema) {


        for (int i = 0; i < this.Unicidade.size(); i++) {


            //já tem uma global com
            if (Unicidade.get(i).getLexema().equals(lexema)) {

                if (Unicidade.get(i).getEscopo1().equals("global")) {
                    return false;
                }
            }

        }
        return true;
    }

    /*
     * retorna true se a variavel foi declarada
     * false se nao foi declarada
     * esse metodo n se preocupa com duplicidade de variavel
     */
    public boolean verificaSeVariavelFoiDeclarada(String lexema) {
        for (int i = 0; i < this.Unicidade.size(); i++) {
            //jÃ¡ tem uma global com
            if (Unicidade.get(i).getLexema().equals(lexema)) {
                return true;
            }
        }
        return false;
    }

    public String RetornaTipoDeGlobalDeclarada(String lexema) {
        for (int i = 0; i < this.Unicidade.size(); i++) {
            if (Unicidade.get(i).getLexema().equals(lexema)) {
                if (Unicidade.get(i).getEscopo1().equals("global")) {
                    return Unicidade.get(i).getTipoVariavel();
                }
            }
        }
        return "";
    }

    public String RetornaTipoDeLocalDeclarada(String lexema, String escopo1) {
        for (int i = 0; i < this.Unicidade.size(); i++) {
            if (Unicidade.get(i).getLexema().equals(lexema)) {
                if (Unicidade.get(i).getEscopo1().equals(escopo1)) {
                    return Unicidade.get(i).getTipoVariavel();
                }
            }
        }
        return "";
    }

    /*
     * No caso de uma chamada de funcao verifica se o metodo existe
     * retorna true se o metodo existir
     */
    public boolean verificaChamadaDeFuncao(String lexema) {

        for (int i = 0; i < this.Unicidade.size(); i++) {
            if (Unicidade.get(i).getLexema().equals(lexema)) {
                return true;
            }
        }
        return false;
    }

    public ArrayList<String> procuraParametrosDeFuncao(String metodo) {

        ArrayList<String> parametros = new ArrayList<String>();

        for (int i = 0; i < this.Unicidade.size(); i++) {
            if (Unicidade.get(i).getLexema().equals(metodo)) {
                parametros = Unicidade.get(i).getParametros();
                return parametros;
            }
        }
        return parametros;

    }

    /**
     *
     * @param escopo
     * @param lexema
     * @return retorna true, se houve uma atribuicao dentro do for
     *         utilizando typedef do tipo inteiro e que ja tenha sido
     *         declarado dentro do metodo ou na parte de declaracao de variaveis globais
     */
    public boolean validaSeForInteiro(String escopo, String lexema) {

        for (int i = 0; i < Unicidade.size(); i++) {
            if (Unicidade.get(i).getEscopo1().equals(escopo) || Unicidade.get(i).getEscopo1().equals("global")) {
                if (Unicidade.get(i).getLexema().equals(lexema)) {
                    if (Unicidade.get(i).getTipoVariavel().equals("int")) {
                        return true;
                    }
                }
            }
        }
        return false;
    }

    /**
     *
     * @param escopo
     * @param lexema
     * @return retorna false, se o nome
     *        não tiver sido declarado dentro de metodo específico ou como global
     */
    public boolean validaNome(String escopo, String lexema) {
        for (int i = 0; i < Unicidade.size(); i++) {
            if (Unicidade.get(i).getEscopo1().equals(escopo) || Unicidade.get(i).getEscopo1().equals("global")) {
                if (Unicidade.get(i).getLexema().equals(lexema)) {
                    return true;
                }
            }
        }
        return false;
    }

    /**Veritica qual o tipo de determinada variável dentro de um metodo
     *
     * @param escopo
     * @param lexema
     * @return retorna false, se o nome
     *        não tiver sido declarado dentro de metodo específico ou como global
     */
    public StringBuffer checagemTipos() {
        StringBuffer buffer = new StringBuffer();


        return buffer;
    }
//------------------------------------------------------------------------------------------------------------

    // parte para debugar
    // para a parte de debug Print
    public Hashtable<Integer, String> getTabelaEscopo() {
        return Escopo;
    }
    //

    public Hashtable<Integer, String> getTabelaTipoVariavel() {
        return TipoVariavel;
    }
    //

    public Hashtable<Integer, String> getTabelaCategoria() {
        return Categoria;
    }
    //
    // ver lookahead

    public String getLexema(int posicao) {
        if (Tokens.containsKey(posicao)) {
            Lookahead = Tokens.get(posicao);
        } else {
            Lookahead = "";
        }
        return Lookahead;
    }

    public void salvaPonteiroParametros() {
        ponteiroParam = ponteiro;
    }

    public void salvaItemListaParametro(String s) {
        listaParametros.add(s);
    }

    public void insereParametro() {
        ParametrosMetodo.put(ponteiroParam, listaParametros);
        ponteiroParam = 0;
        listaParametros = new ArrayList<String>();
    }
    // var ultimo escopo
    private String var_ultimoEscopo;
    // guarda o ultimo tipoVaravel

    public void setUltimoTipoVar(String escopo) {
        var_ultimoEscopo = escopo;
    }

    public String getUltmoTipoV() {
        return var_ultimoEscopo;
    }
}
